#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Azure DevOps 客户端封装
支持个人访问令牌(PAT)认证方式
"""

import os
import requests
import json
import base64
from urllib.parse import urljoin
import shutil


class AzureDevOpsClient:
    """
    Azure DevOps REST API 客户端
    使用个人访问令牌(PAT)进行认证
    """
    
    def __init__(self, organization, username, pat_token, server_url=None):
        """
        初始化Azure DevOps客户端
        
        Args:
            organization: Azure DevOps组织名称
            username: 用户名或邮箱
            pat_token: 个人访问令牌
            server_url: 服务器URL，默认为https://dev.azure.com
        """
        self.organization = organization
        self.username = username
        self.pat_token = pat_token
        self.server_url = server_url or "https://dev.azure.com"
        
        # 设置认证头
        self.auth_header = self._create_auth_header()
        
        # API基础URL
        self.api_base_url = f"{self.server_url}/{organization}/_apis"
        
    def _create_auth_header(self):
        """创建认证头"""
        # Azure DevOps使用Basic认证，用户名和PAT的组合
        credentials = f"{self.username}:{self.pat_token}"
        encoded_credentials = base64.b64encode(credentials.encode()).decode()
        return {
            'Authorization': f'Basic {encoded_credentials}',
            'Content-Type': 'application/json'
        }
    
    def test_connection(self):
        """
        测试连接是否正常
        
        Returns:
            tuple: (success: bool, message: str)
        """
        try:
            url = f"{self.api_base_url}/projects"
            response = requests.get(url, headers=self.auth_header, timeout=10)
            
            if response.status_code == 200:
                return True, "连接成功"
            elif response.status_code == 401:
                return False, "认证失败，请检查用户名和PAT令牌"
            elif response.status_code == 404:
                return False, f"组织 '{self.organization}' 不存在"
            else:
                return False, f"连接失败，状态码: {response.status_code}"
                
        except requests.exceptions.Timeout:
            return False, "连接超时"
        except requests.exceptions.ConnectionError:
            return False, "网络连接错误"
        except Exception as e:
            return False, f"连接测试失败: {str(e)}"
    
    def get_projects(self):
        """
        获取项目列表
        
        Returns:
            tuple: (success: bool, projects: list or error_message: str)
        """
        try:
            url = f"{self.api_base_url}/projects"
            response = requests.get(url, headers=self.auth_header, timeout=10)
            
            if response.status_code == 200:
                data = response.json()
                projects = []
                for project in data.get('value', []):
                    projects.append({
                        'id': project.get('id'),
                        'name': project.get('name'),
                        'description': project.get('description', ''),
                        'url': project.get('url')
                    })
                return True, projects
            else:
                return False, f"获取项目列表失败，状态码: {response.status_code}"
                
        except Exception as e:
            return False, f"获取项目列表失败: {str(e)}"
    
    def get_repositories(self, project_name):
        """
        获取指定项目的仓库列表
        
        Args:
            project_name: 项目名称
            
        Returns:
            tuple: (success: bool, repositories: list or error_message: str)
        """
        try:
            url = f"{self.api_base_url}/git/repositories"
            params = {'api-version': '6.0'}
            response = requests.get(url, headers=self.auth_header, params=params, timeout=10)
            
            if response.status_code == 200:
                data = response.json()
                repositories = []
                for repo in data.get('value', []):
                    if repo.get('project', {}).get('name') == project_name:
                        repositories.append({
                            'id': repo.get('id'),
                            'name': repo.get('name'),
                            'url': repo.get('url'),
                            'remoteUrl': repo.get('remoteUrl'),
                            'defaultBranch': repo.get('defaultBranch', 'refs/heads/main')
                        })
                return True, repositories
            else:
                return False, f"获取仓库列表失败，状态码: {response.status_code}"
                
        except Exception as e:
            return False, f"获取仓库列表失败: {str(e)}"
    
    def get_repository_items(self, project_name, repository_name, path="/"):
        """
        获取仓库文件列表
        
        Args:
            project_name: 项目名称
            repository_name: 仓库名称
            path: 路径，默认为根目录
            
        Returns:
            tuple: (success: bool, items: list or error_message: str)
        """
        try:
            url = f"{self.api_base_url}/git/repositories/{repository_name}/items"
            params = {
                'api-version': '6.0',
                'scopePath': path,
                'recursionLevel': 'OneLevel'
            }
            response = requests.get(url, headers=self.auth_header, params=params, timeout=10)
            
            if response.status_code == 200:
                data = response.json()
                items = []
                for item in data.get('value', []):
                    items.append({
                        'path': item.get('path'),
                        'isFolder': item.get('isFolder'),
                        'size': item.get('size'),
                        'url': item.get('url')
                    })
                return True, items
            else:
                return False, f"获取文件列表失败，状态码: {response.status_code}"
                
        except Exception as e:
            return False, f"获取文件列表失败: {str(e)}"
    
    def download_file(self, project_name, repository_name, file_path, local_path):
        """
        下载文件
        
        Args:
            project_name: 项目名称
            repository_name: 仓库名称
            file_path: 文件路径
            local_path: 本地保存路径
            
        Returns:
            tuple: (success: bool, message: str)
        """
        try:
            url = f"{self.api_base_url}/git/repositories/{repository_name}/items"
            params = {
                'api-version': '6.0',
                'path': file_path,
                'download': 'true'
            }
            response = requests.get(url, headers=self.auth_header, params=params, timeout=30)
            
            if response.status_code == 200:
                # 确保目录存在
                os.makedirs(os.path.dirname(local_path), exist_ok=True)
                
                with open(local_path, 'wb') as f:
                    f.write(response.content)
                return True, f"文件下载成功: {local_path}"
            else:
                return False, f"下载文件失败，状态码: {response.status_code}"
                
        except Exception as e:
            return False, f"下载文件失败: {str(e)}"
    
    def get_file_content(self, project_name, repository_name, file_path):
        """
        获取文件内容
        
        Args:
            project_name: 项目名称
            repository_name: 仓库名称
            file_path: 文件路径
            
        Returns:
            tuple: (success: bool, content: str or error_message: str)
        """
        try:
            url = f"{self.api_base_url}/git/repositories/{repository_name}/items"
            params = {
                'api-version': '6.0',
                'path': file_path
            }
            response = requests.get(url, headers=self.auth_header, params=params, timeout=10)
            
            if response.status_code == 200:
                return True, response.text
            else:
                return False, f"获取文件内容失败，状态码: {response.status_code}"
                
        except Exception as e:
            return False, f"获取文件内容失败: {str(e)}"
    
    def clone_repository(self, repository_url, local_path, branch="main"):
        """
        克隆仓库到本地（需要安装git）
        
        Args:
            repository_url: 仓库URL
            local_path: 本地路径
            branch: 分支名称
            
        Returns:
            tuple: (success: bool, message: str)
        """
        try:
            import subprocess
            
            # 确保目录不存在
            if os.path.exists(local_path):
                shutil.rmtree(local_path)
            
            # 构建git命令
            # 将URL中的用户名替换为PAT令牌
            if self.username in repository_url:
                repo_url_with_token = repository_url.replace(
                    f"https://{self.username}@", 
                    f"https://{self.username}:{self.pat_token}@"
                )
            else:
                repo_url_with_token = repository_url.replace(
                    "https://", 
                    f"https://{self.username}:{self.pat_token}@"
                )
            
            # 执行git clone
            cmd = ["git", "clone", "-b", branch, repo_url_with_token, local_path]
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
            
            if result.returncode == 0:
                return True, f"仓库克隆成功: {local_path}"
            else:
                return False, f"克隆失败: {result.stderr}"
                
        except subprocess.TimeoutExpired:
            return False, "克隆超时"
        except FileNotFoundError:
            return False, "未找到git命令，请确保已安装git"
        except Exception as e:
            return False, f"克隆失败: {str(e)}"
    
    def get_commits(self, project_name, repository_name, branch="main", top=10):
        """
        获取提交历史
        
        Args:
            project_name: 项目名称
            repository_name: 仓库名称
            branch: 分支名称
            top: 返回的提交数量
            
        Returns:
            tuple: (success: bool, commits: list or error_message: str)
        """
        try:
            url = f"{self.api_base_url}/git/repositories/{repository_name}/commits"
            params = {
                'api-version': '6.0',
                'branch': branch,
                '$top': top
            }
            response = requests.get(url, headers=self.auth_header, params=params, timeout=10)
            
            if response.status_code == 200:
                data = response.json()
                commits = []
                for commit in data.get('value', []):
                    commits.append({
                        'commitId': commit.get('commitId'),
                        'author': commit.get('author', {}).get('name'),
                        'message': commit.get('comment'),
                        'date': commit.get('author', {}).get('date')
                    })
                return True, commits
            else:
                return False, f"获取提交历史失败，状态码: {response.status_code}"
                
        except Exception as e:
            return False, f"获取提交历史失败: {str(e)}"
